#include "mboot.h"

/* mboot 私有数据 */
mboot_prvt s_mboot_prvt = 
{
    .mw                = NULL,               /* 中间件 */
    .evesel_status     = {MB_FALSE_DEF, MB_FALSE_DEF, MB_FALSE_DEF},  /* 每个项的选择状态 */
    .SELECTS_STATUS    = MB_FALSE_DEF,       /* 所有选项状态 */
    .UPDATE_STATUS     = MB_XFR_NOTSTART,    /* 升级状态 */
    .is_delay_stoped   = MB_FALSE_DEF,
    .is_need_show_menu = MB_TRUE_DEF,       /* 第一次自动显示菜单 */
};

/* ---------------------- 人机交互选择实现 ---------------------- */

/* 有条件设置升级状态 */
static void mboot_set_update_status_cond(MB_XFR_STATUS STATUS)
{
    /* 状态只能设置一次 */
    if (s_mboot_prvt.UPDATE_STATUS != STATUS)
    {
        s_mboot_prvt.UPDATE_STATUS = STATUS;
    }

    return;
}

/* 获得升级状态 */
static MB_XFR_STATUS mboot_get_update_status(void)
{
    return s_mboot_prvt.UPDATE_STATUS;
}

/* 非阻塞式, 接收固件数据并将数据存储到设备中 */
static MB_XFR_STATUS mboot_op_recv_and_store(void)
{
    mb_mw *          mw = s_mboot_prvt.mw;
    mb_buf_attr *    buf_attr;
    MB_XFR_STATUS    XFR_STATUS;
    static uint32_t  rx_cnt = 0;

    /* 判断所有选项是否都已设置 */
    if (s_mboot_prvt.SELECTS_STATUS != MB_TRUE)
    {
        return MB_XFR_MODERR;   /* 模块异常 */
    }

    /* 接收数据, 解析数据 */
    buf_attr = mw->prot->transfer_and_parse();

    /* 数据有效, 存入目标存储器 */
    if (buf_attr != NULL)
    {
        /* 将接收到的数据拷贝至缓冲区 */
        memcpy((s_mboot_prvt.buf_attr.buf + rx_cnt), buf_attr->buf, buf_attr->len);
        rx_cnt += buf_attr->len;

        /* - 收集满1页才写入存储设备, 
         * - 或则 0 < len < (1/2 page, 即为最后一帧), 驱动底层应始终按一页的数据量写入 */
        if (((rx_cnt / s_mboot_prvt.buf_attr.len) == 1) || \
            ((0 < buf_attr->len) && (buf_attr->len < PROT_PAYLOAD_SIZE)))
        {
            /* 写入存储设备 */
            mw->store->store_firmware(&s_mboot_prvt.buf_attr);

            /* 清空缓冲区和计数值 */
            memset(s_mboot_prvt.buf_attr.buf, 0, s_mboot_prvt.buf_attr.len);
            rx_cnt = 0;
        }
    }
    
    /* 检测数据传输状态 */
    XFR_STATUS = mw->prot->get_xfr_status();

    /* 更新升级状态 */
    mboot_set_update_status_cond(XFR_STATUS);

    return XFR_STATUS;
}

/* 加载固件数据到内部ROM, 然后系统重启 (针对固件需要存储在片内ROM的设备) */
static void mboot_helper_system_load(uint32_t start_addr)
{
    mb_mw * mw = s_mboot_prvt.mw;

    /* 加载固件 */
    mw->preboot->load_firmware(start_addr);

    /* 准备跳转到 APP */

    return;
}

/* 引导新系统 (针对固件本就需要存储在外部存储器的设备) */
static void mboot_helper_system_boot(void)
{
    int     ret;
    mb_mw * mw = s_mboot_prvt.mw;
    void (*jump_to_app)(void);         /* 声明一个函数指针 */
    __IO uint32_t mmap_addr = MB_FW_QSPI_STO_MMAP_ADDR;

    /* 建立存储设备内存映射 */
    ret = mw->preboot->memory_mapped();

    if (ret != 0)
    {
        return;
    }

    /* 去初始化 */
    mw->preboot->system_deinit(mmap_addr);

    /* 跳转到应用程序，首地址是MSP，地址+4是复位中断服务程序地址 */
    jump_to_app = (void (*)(void)) (*((__IO uint32_t *) (mmap_addr + 4)));

    /* 设置新的栈地址 */
    __set_MSP(*(__IO uint32_t*) mmap_addr);

    /* 跳转到应用程序地址 */
    jump_to_app();

    while (1) 
    {
        /* cannot be here */
        ;
    }
}

/* 进入新系统 */
static void mboot_op_entry_new_system(void)
{
    STO_SELECT RSEL;
    mb_mw *    mw = s_mboot_prvt.mw;

    /* 变量判断 */
    if (mw == NULL)
    {
        return;
    }

    /* 获得选择的存储设备类型 */
    RSEL = mw->store->get_carrier_select();

    if (RSEL != STO_QSPIFLASH)
    {
        /* 固件数据最终需要存储在片类, 则需要先加载到内部ROM */
        mboot_helper_system_load(MB_FW_MOVE2ROM_START_ADDR);
    }
    else
    {
        /* 固件存储在片外, 则需要引导系统启动 */
        mboot_helper_system_boot();
    }

    return;
}

/* ---------------------- 人机交互选择执行 ---------------------- */

/* 选项初始化 */
static int mboot_selects_init(PROT_SELECT protsel, CMC_SELECT cmcsel, \
                              STO_SELECT stosel, MB_STATUS STO_ERASE)
{
    mb_mw *   mw = s_mboot_prvt.mw;
    MB_STATUS R_STATUS;
    uint32_t  temp;

    /* 只有未开始时, 才能设置 */
    if (mboot_get_update_status() != MB_XFR_NOTSTART)
    {
        return -1;
    }

    /* ------------- 选择数据协议 ------------- */
    {
        /* 有效性判断 */
        if (protsel < PROT_UNKNOWN)
        {
            /* 选择数据协议 */
            R_STATUS = mw->prot->prot_select(protsel);

            if (R_STATUS != MB_TRUE)
            {
                return -1;   /* 模块错误 */
            }

            /* 标记选择状态 */
            s_mboot_prvt.evesel_status.is_prot_sel = MB_TRUE;
        }
    }

    /* ----------- 选择数据协议的数据载体 ----------- */
    {
        /* 有效性判断 */
        if (cmcsel < CMC_UNKNOWN)
        {
            /* 选择数据协议的数据载体 */
            R_STATUS = mw->prot->carrier_select(cmcsel);

            if (R_STATUS != MB_TRUE)
            {
                return -1;   /* 模块错误 */
            }

            /* 标记选择状态 */
            s_mboot_prvt.evesel_status.is_cmc_sel = MB_TRUE;
        }
    }

    /* ----------- 选择数据存储的载体 ----------- */
    {
        if (stosel < STO_UNKNOWN)
        {
            if (STO_ERASE == MB_TRUE)
            {
                temp = MB_FW_MAX_SIZE;   /* 选中 + 擦除 */
            }
            else
            {
                temp = 0;   /* 仅选中, 不擦除 */
            }

            /* 选择存储设备 */
            R_STATUS = mw->store->carrier_select(stosel, temp);

            if (R_STATUS != MB_TRUE)
            {
                return -1;   /* 模块错误 */
            }

            /* 标记选择状态 */
            s_mboot_prvt.evesel_status.is_sto_sel = MB_TRUE;
        }
    }

    /* 判断所有选项的最终状态 */
    if ((s_mboot_prvt.evesel_status.is_prot_sel == MB_TRUE) && \
        (s_mboot_prvt.evesel_status.is_cmc_sel == MB_TRUE) && \
        (s_mboot_prvt.evesel_status.is_sto_sel == MB_TRUE))
    {
        /* 标记所有选项已选择 */
        s_mboot_prvt.SELECTS_STATUS = MB_TRUE;
    }

    return 0;
}

/* 更新固件 (阻塞式) */
static int mboot_update_firmware(void)
{
    MB_XFR_STATUS XFR_STATUS = MB_XFR_NOTSTART;

    /* 设置升级状态 */
    mboot_set_update_status_cond(MB_XFR_NOTSTART);

    /* 阻塞式接收固件并写入存储设备 */
    while (1)
    {
        /* 非阻塞式, 接收固件数据并将数据存储到设备中 */
        XFR_STATUS = mboot_op_recv_and_store();

        if (XFR_STATUS == MB_XFR_MODERR)
        {
            return -1;  /* 出错返回 */
        }

        if (XFR_STATUS == MB_XFR_FINISHED)
        {
            break;  /* 执行完毕 (成功) */
        }
    }

    return 0;
}

/* 进入APP (升级完毕) */
static int mboot_entry_app(void)
{
    MB_XFR_STATUS XFR_STATUS;

    /* 获得升级状态 */
    XFR_STATUS = mboot_get_update_status();

    /* 进入APP的先决条件(满足之一即可): 
     * - 所有选项都已选择 (不擦除存储设备)
     * - 已接收完毕新固件
     */
    if ((s_mboot_prvt.SELECTS_STATUS != MB_TRUE) && \
        (XFR_STATUS != MB_XFR_FINISHED))
    {
        return -1;   /* 模块异常 */
    }

    /* 进入新系统 */
    mboot_op_entry_new_system();

    return 0;
}

/* 系统复位 */
static int mboot_system_reset(void)
{
    /* 不应该直接操作这个函数 */
    HAL_NVIC_SystemReset();

    return 0;
}

/* 退出MBOOT (按默认执行) */
static int mboot_exit(void)
{
    return -1;
}

/* ------------------- 提供给用户的接口实现 ------------------- */

/* mboot 初始化 */
static int mboot_init(void)
{
    /* 中间件初始化 (获得操作接口) */
    s_mboot_prvt.mw = mb_mw_init();

    if (s_mboot_prvt.mw  == NULL)
    {
        return -1;    /* 初始化失败 */
    }
    
    /* 初始化缓冲区 */
    s_mboot_prvt.buf_attr.buf = s_mboot_prvt.write_buf;
    s_mboot_prvt.buf_attr.len = sizeof(s_mboot_prvt.write_buf);

    /* 初始化成功 */
    return 0;
}

/* 启动延迟判断是否进入 mboot */
static int mboot_boot_delay(int delay_s)
{
    mb_mw *   mw    = s_mboot_prvt.mw;
    hci_req * req = NULL;
    uint8_t   cmd1, cmd2, cmd3;
    int       ret;
    MB_STATUS RSTATUS;

    if (delay_s == 0)
    {
        return 0;   /* 不进入延时 */
    }

    /* 选择人机交互数据通道 */
    if (mw->hci->carrier_select(HCI_CMC_SELECT_DEFAULT) != 0)
    {
        return 0;
    }

    /* 处理人机交互指令 */
    while (1)
    {
        /* 检测在等待用户打断信号之前, 倒计时是否超时 */
        if (s_mboot_prvt.is_delay_stoped == MB_FALSE)
        {
            if (mw->hci->check_is_timeout(delay_s) == MB_TRUE)
            {
                break;  /* 超时退出 */
            }

            /* 等待用户打断 (MB_TRUE: 显示延时提示) */
            RSTATUS = mw->hci->wait_abort(MB_TRUE);

            if (RSTATUS == MB_TRUE)
            {
                s_mboot_prvt.is_delay_stoped = MB_TRUE;
            }

            continue;   /* 被用户打断, 进入BOOT MENU */
        }

        /* 打印 BOOT MENU */
        if (s_mboot_prvt.is_need_show_menu == MB_TRUE)
        {
            mw->hci->menu_show();
            
            /* 只显示1次 */
            s_mboot_prvt.is_need_show_menu = MB_FALSE;
        }

        /* 接收用户指令并解析 */
        req = mw->hci->select_parse();

        if (req == NULL)
        {
            continue;   /* 未接收到用户指令 */
        }

        /* 判断是否是请求重新输入 */
        if (req->is_reselect == MB_TRUE)
        {
            /* 再次允许显示MENU */
            s_mboot_prvt.is_need_show_menu = MB_TRUE;
            continue;
        }

        /* 解析各级指令 */
        cmd1 = HCI_1ST_REQ_BIT_DEDOMAIN(req->bits);
        cmd2 = HCI_2ND_REQ_BIT_DEDOMAIN(req->bits);
        cmd3 = HCI_3RD_REQ_BIT_DEDOMAIN(req->bits);

        switch (cmd1)
        {
            case HCI_REQ_SELECT_PROT:
            {
                ret = mboot_selects_init((PROT_SELECT) cmd2, \
                                         CMC_UNKNOWN, \
                                         STO_UNKNOWN, MB_FALSE);
            } break;

            case HCI_REQ_SELECT_CMC:
            {
                ret = mboot_selects_init(PROT_UNKNOWN, \
                                         (CMC_SELECT) cmd2, \
                                         STO_UNKNOWN, MB_FALSE);
            } break;

            case HCI_REQ_SELECT_STO:
            {
                ret = mboot_selects_init(PROT_UNKNOWN, \
                                         CMC_UNKNOWN, \
                                         (STO_SELECT) cmd2, (MB_STATUS) cmd3);
            } break;

            case HCI_REQ_UPDATE_FW:
            {
                ret = mboot_update_firmware();
            } break;

            case HCI_REQ_ENTRY_APP:
            {
                ret = mboot_entry_app();
            } break;

            case HCI_REQ_RESET:
            {
                ret = mboot_system_reset();
            } break;

            case HCI_REQ_EXIT_BOOT:
            {
                ret = mboot_exit(); /* 暂未实现 */
            } break;

            default:
                /* 下层已对数据有效性进行判断, 所以不会进入default */
                ret = -1;
                break;
        }

        /* 给用户反馈执行结果 */
        RSTATUS = (ret == 0) ? MB_TRUE : MB_FALSE;
        mw->hci->response(RSTATUS);

        /* 再次允许显示MENU */
        s_mboot_prvt.is_need_show_menu = MB_TRUE;
    }

    return 0;
}

/* 打印出新的 固件 bin 文件的数据 */
static int mboot_op_test_print_newbin(void)
{
#define MB_PRIN_FROM 0xFF   /* 选择调试模式 */

#if (MB_PRIN_FROM == 0)  /* 从接收缓冲区打印 */
    printf("len = %d\n", bin_len);
    for (uint32_t i = 0; i < bin_len; i++)
    {
        if ((i % 16) == 0)
        {
            printf("\n");
        }
        printf("%.2X", bin_buf[i]);
    }
    printf("\nover!!!!\n\n");
    return 0;
#elif (MB_PRIN_FROM == 1)  /* 从QSPI中读出打印 */
    mb_mw *     mw = s_mboot_prvt.mw;
    mb_buf_attr * buf_attr;

    buf_attr = mw->store->get_firmware();
    for (uint32_t i = 0; i < buf_attr->len; i++)
    {
        if ((i % 16) == 0)
        {
            printf("\n");
        }

        printf("%.2X", buf_attr->buf[i]);
    }
    printf("\nover!!!!\n\n");
    return 0;
#elif (MB_PRIN_FROM == 2)  /* 从QSPI内存映射中获取打印 */
    mb_buf_attr * buf_attr;
    uint8_t *ptr = (uint8_t *) MB_FW_QSPI_STO_MMAP_ADDR;

    for (uint32_t i = 0; i < MB_FW_MAX_SIZE; i++)
    {
        if ((i % 16) == 0)
        {
            printf("\n");
        }

        printf("%.2X", ptr[i]);
    }
    printf("\nover!!!!\n\n");
    return 0;
#endif

    return -1;
}

/* mboot 操作接口 */
mboot_op s_mboot_op = 
{
    .init              = mboot_init,
    .boot_delay        = mboot_boot_delay,

    /* ------ test for developer ------- */
    .test_print_newbin = mboot_op_test_print_newbin,
};

/* mboot 操作接口注册 */
mboot_op *mboot_op_register(void)
{
    return &s_mboot_op;
}
